﻿#Add-PSSnapIn Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
if ((Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null ) {
	Add-PSSnapin Microsoft.SharePoint.Powershell
}

# Resolve IP address from Hostname 
# https://gallery.technet.microsoft.com/scriptcenter/44e9fef7-a04b-40b3-bb05-97659e56e27e
function Get-IPAddress {
#Requires -Version 2.0            
	[CmdletBinding()]            
	Param(                       
		[Parameter(Position=1,
				   ValueFromPipeline=$true,
				   ValueFromPipelineByPropertyName=$true)]
		[String]$HostName = $env:COMPUTERNAME,
		[Switch]$IPV6only,
		[Switch]$IPV4only
    )
	Try {
		$AddressList = @(([net.dns]::GetHostEntry($HostName)).AddressList)
	} Catch {
		Write-Warning ("Cannot determine the IP Address on $HostName")
	}
	if ($AddressList.Count -ne 0) {
		$AddressList | ForEach-Object {
			if ($IPV6only) {			
				if ($_.AddressFamily -eq "InterNetworkV6") {
					New-Object psobject -Property @{
						IPAddress    = $_.IPAddressToString
						ComputerName = $HostName
					}
				}
			}
			if ($IPV4only) {
				if ($_.AddressFamily -eq "InterNetwork") {
					New-Object psobject -Property @{
						IPAddress    = $_.IPAddressToString
						ComputerName = $HostName
					}
				}
			}
			if (!($IPV6only -or $IPV4only)) {
				New-Object psobject -Property @{
					IPAddress    = $_.IPAddressToString
					ComputerName = $HostName
				}
			}
		}
	}
}

# Carbon HostsFile
# http://get-carbon.org/

function Get-HostsPath {
	<#
    Gets the path to this computer's hosts file.
    
    .DESCRIPTION
    This is a convenience method so you don't have to have the path to the hosts file hard-coded in your scripts.
    
    .EXAMPLE
    Get-HostsPath
    
    Returns `C:\Windows\system32\drivers\etc\hosts`.  Uses the environment variable to find the root to the Windows directory.
    #>
    return Join-Path $env:windir system32\drivers\etc\hosts
}

function Set-HostsEntry {
	<#
    Sets a hosts entry in a hosts file.
    
    .DESCRIPTION
    Sets the IP address for a given hostname.  If the hostname doesn't exist in the hosts file, appends a new entry to the end.  If the hostname does exist, its IP address gets updated.  If you supply a description, it is appended to the line as a comment.
    
    If any duplicate hosts entries are found, they are commented out; Windows uses the first duplicate entry.
    
    This function scans the entire hosts file.  If you have a large hosts file, and are updating multiple entries, this function will be slow.
    
    You can operate on a custom hosts file, too.  Pass its path with the `Path` parameter.

    Sometimes the system's hosts file is in use and locked when you try to update it. The `Set-HostsEntry` function tries 10 times to set a hosts entry before giving up and writing an error. It waits a random amount of time (from 0 to 1000 milliseconds) between each attempt.
    
    .EXAMPLE
    Set-HostsEntry -IPAddress 10.2.3.4 -HostName 'myserver' -Description "myserver's IP address"
    
    If your hosts file contains the following:
    
        127.0.0.1  localhost
        
    After running this command, it will contain the following:
    
        127.0.0.1        localhost
        10.2.3.4         myserver	# myserver's IP address

    .EXAMPLE        
    Set-HostsEntry -IPAddress 10.5.6.7 -HostName 'myserver'
    
    If your hosts file contains the following:
    
        127.0.0.1        localhost
        10.2.3.4         myserver	# myserver's IP address
    
    After running this command, it will contain the following:
    
        127.0.0.1        localhost
        10.5.6.7         myserver
    
    #>
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true)]
        [ValidatePattern('\A(?:\b(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\b)\Z')]
        [string]
        # The IP address for the hosts entry.
        $IPAddress,

        [Parameter(Mandatory=$true)]
        [string]
        # The hostname for the hosts entry.
        $HostName,

        [string]
        # An optional description of the hosts entry.
        $Description,

        [string]
        # The path to the hosts file where the entry should be set. Defaults to the local computer's hosts file.
        $Path = (Get-HostsPath)
    )
 
    $matchPattern = '^(?<IP>[0-9a-f.:]+)\s+(?<HostName>[^\s#]+)(?<Tail>.*)$'  
    $lineFormat = "{0,-16}{1}{2}"
    
    if(-not (Test-Path $Path)) {
        Write-Warning "Creating hosts file at: $Path"
        New-Item $Path -ItemType File
    }
     
    $lines = @( Get-Content -Path $Path )
    $outLines = New-Object System.Collections.ArrayList
    $found = $false
    $lineNum = 0
     
    foreach($line in $lines) {
        $lineNum += 1        
        if($line.Trim().StartsWith("#") -or ($line.Trim() -eq '')) {
            [void] $outlines.Add($line)
        }
        elseif($line -match $matchPattern) {
            $ip = $matches["IP"]
            $hn = $matches["HostName"]
            $tail = $matches["Tail"].Trim()
            if($HostName -eq $hn) {
                if($found) {
                    #this is a duplicate so, let's comment it out
                    [void] $outlines.Add("#$line")
                    continue
                }
                $ip = $IPAddress
                $tail = if( $Description ) { "`t# $Description" } else { '' }
                $found = $true   
            }
           
            if($tail.Trim() -eq "#") {
                $tail = ""
            }
           
            $outline = $lineformat -f $ip, $hn, $tail
            [void] $outlines.Add($outline)                
        }
        else {
            Write-Warning ("Hosts file {0}: line {1}: invalid entry: {2}" -f $Path, $lineNum, $line)
            $outlines.Add( ('# {0}' -f $line) )
        }
    }
     
    if(-not $found) {
       #add a new entry
       $tail = "`t# $Description"
       if($tail.Trim() -eq "#") {
           $tail = ""
       }
           
       $outline = $lineformat -f $IPAddress, $HostName, $tail
       [void] $outlines.Add($outline)
    }
    
    if($pscmdlet.ShouldProcess( $Path, "set hosts entry $HostName to point to $IPAddress")) {
        $succeeded = $false
        $maxTries = 10
        $rng = New-Object 'Random'
        for( $idx = 0; $idx -lt $maxTries; ++$idx ) {
            $exception = $false
            try {
                $setHostsEntryError = @()
                $outlines | Out-File -FilePath $Path -Encoding OEM -ErrorAction SilentlyContinue -ErrorVariable 'setHostsEntryError'
                $succeeded = $true
                break
            } catch {
                if($Global:Error.Count -gt 0) {
                    $Global:Error.RemoveAt(0)
                }
                $exception = $true
            }

            if( $exception -or $setHostsEntryError ) {
                $timeout = $rng.Next(0,1000)
                Write-Verbose ('Failed to set hosts entry ''{0}    {1}'' in ''{2}'': waiting {3} milliseconds to try again.' -f $HostName,$IPAddress,$Path,$timeout)
                Start-Sleep -Milliseconds $timeout
            }
        }

        if( -not $succeeded ) {
            Write-Error ('Failed to set hosts entry ''{0} {1}'' in ''{2}'': looks like the hosts file is in use.' -f $HostName,$IPAddress,$Path)
        }
        
    }     
}

function Remove-HostsEntry {
    <# 
    Removes entries from the hosts file by hostname.

    .DESCRIPTION
    You can remove multiple entries or pipe entries into this function.

    .EXAMPLE
    Remove-HostsEntry -HostName 'adadvisor.net'

    Demonstrates how to remove hosts entry for `adadvisor.net`, which you probably don't want to do.

    .EXAMPLE
    Remove-HostsEntry -HostName 'adadvisor.net','www.adchimp.com'

    Demonstrates how to remove multiple hosts entries.

    .EXAMPLE
    ('adadvisor.net','www.adchimp.com') | Remove-HostsEntry

    Demonstrates how to pipe hostnames into `Remove-HostsEntry`.

    .EXAMPLE
    Remove-HostsEntry -HostName 'adadvisor.net' -Path 'C:\Projects\Carbon\adblockhosts'

    Demonstrates how to work with a file other than Windows' default hosts file.
    #>
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)]
        [string[]]
        # The hostname of the hosts entry/entries to remove.
        $HostName,

        [string]
        # The hosts file to modify.  Defaults to the Windows hosts file.
        $Path = (Get-HostsPath)
    )

    begin {
        Set-StrictMode -Version 'Latest'
        $allHostNames = New-Object 'Collections.ArrayList'
    } process {
        $HostName | 
            ForEach-Object { [Text.RegularExpressions.Regex]::Escape( $_ ) } |
            ForEach-Object { [void] $allHostNames.Add( $_ ) }
    } end {
        $regex = $allHostNames -join '|'
        $regex = '^[0-9a-f.:]+\s+\b({0})\b.*$' -f $regex 

        $newHostsFile = Get-Content -Path $Path |
                            Where-Object { $_ -notmatch $regex }

        $entryNoun = 'entry'
        if( $HostName.Count -gt 1 ) {
            $entryNoun = 'entries'
        }

        if( $PSCmdlet.ShouldProcess( $Path, ('removing hosts {0} {1}' -f $entryNoun,($HostName -join ', ')) ) ) {
            Set-Content -Path $Path -Value $newHostsFile
        }
    }
}

function Reset-Hosts {
    <#
    Removes all custom host entries from this computer's hosts file.
    
    .DESCRIPTION
    Sometimes you want to start over.  This method removes all hosts entries from your hosts file after the default localhost entry.
    
    By default, the current computer's hosts file is reset.  You can operate on a custom hosts file by passing its path to the `Path` argument.
    
    .EXAMPLE
    Reset-Hosts
    
    If your hosts file contains something like this:
    
        127.0.0.1        localhost
        10.1.2.3         myserver
        10.5.6.7         myserver2
        
    After calling `Reset-HostsFile`, your hosts will contain:
    
        127.0.0.1        localhost
      
    .EXAMPLE
    Reset-HostsFile -Path my\custom\hosts
    
    Resets the hosts file at `my\custom\hosts`.
    #>
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
       [string]
       # The path to the hosts file to modify.  Defaults to the local computer's hosts file.
       $Path = (Get-HostsPath)
    )

    Set-StrictMode -Version 'Latest'
 
    if(-not (Test-Path $Path)) {
       Write-Warning "Creating hosts file '$Path'."
       New-Item $Path -ItemType File
    }
    
    $lines = @( Get-Content -Path $Path )
    $outLines = New-Object System.Collections.ArrayList

    foreach($line in $lines) {     
        if($line.Trim().StartsWith("#") -or ($line.Trim() -eq '')) {
            [void] $outlines.Add($line)
        } else {
            break
        }
    }    
	#[void] $outlines.Add("127.0.0.1		localhost")
    if($PSCmdlet.ShouldProcess( $Path, "Reset-HostsFile" )) {
        $outlines | Out-File -FilePath $Path -Encoding OEM
    }     
}

function Warmup {
	[CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true)]
        [ValidatePattern("^(http|https)\://*")]
        [string]
        # The url for the sharepoint site.
        $Url
	)

    # Opening Internet Explorer...
	$global:ie = New-Object -com "InternetExplorer.Application"
	$global:ie.Navigate("about:blank")
	$global:ie.visible = $true
    $global:ieproc = (Get-Process -Name iexplore)
	# Navigate to the url
	Write-Host "  Navigating to $url"
	try {
		$global:ie.Navigate($url)
	} catch {
		# Closing Internet Explorer...
		if ($global:ie) {
			Write-Warning("  IE not responding. Closing Internet Explorer...")
			$global:ie.Quit()
			$global:ieproc | Stop-Process -Force
			Warmup -Url $Url
		}
	}
	WaitForLoading
	# Closing Internet Explorer...
	if ($global:ie) {
		$global:ie.Quit()
        $global:ieproc | Stop-Process -Force
	}
	# Cleaning temporary internet files	
	$cachePath = Join-Path $env:userprofile AppData\Local\Microsoft\Windows\INetCache
    Remove-item  $cachePath -Recurse -ErrorAction SilentlyContinue
}

function WaitForLoading {
	[CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [int]
        $DelayTime = 1000,
		[int]
        $Maxloop = 60
	)

	# Wait for current page to finish loading
	$loaded = $false
	$loop = 0
	while ($loaded -eq $false) {
		$loop++
		if ($loop -gt $Maxloop) {
			$loaded = $true
		}
		[System.Threading.Thread]::Sleep($DelayTime) 
		# If the browser is not busy, the page is loaded
		if (-not $global:ie.Busy) {
			$loaded = $true
		} 
	}
    # Make sure
    [System.Threading.Thread]::Sleep($DelayTime)
}

#修改默认身份验证服务器地址，能够不需验证就能够登陆。（针对双因子验证，Windows、Form验证需修改）
Set-HostsEntry -IPAddress "10.30.47.172" -HostName "pltmuep.erp.petrochina"

while($true){
	Get-SPServer | ? { $_.Role -eq "Application" } | ForEach-Object { 
        $server = $_.Address;
		$ip = (Get-IPAddress -HostName $server -IPV4only | select -first 1).IPAddress
		Write-Host "Warm up the front-end server: $ip"
        
	    # Warmup SharePoint web applications
	    Write-Host " Opening Web Applications..."
	    $webApps = Get-SPWebApplication
	    foreach ($webApp in $webApps) {
	    	foreach($site in $webApp.Sites) {
                #仅预热激活发布功能的门户网站，预热子站点可继续循环，还可以自定义加热其他页面
	    		$IsPublishingSite = [Microsoft.SharePoint.Publishing.PublishingSite]::IsPublishingSite($site)
	    		if($IsPublishingSite){
                    Set-HostsEntry -IPAddress $ip -HostName $site.PrimaryUri.Host
	    			Write-Host "  Warm up $site"
	    			Warmup($site.Url)
	    		}
	    	}
	    }
        
	    Write-Host "Opening Custom URLs..."
	    Warmup("http://" + $ip + ":32843/Topology/topology.svc")
		Warmup("http://" + $ip + ":32843/b14b88037c8f4871b3cd40cc1962884e/MetadataWebService.svc")
	}
    Write-Host "Finishing Warmup..."
}

Reset-Hosts